package com._21cn.framework.utils;

import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;


/**
 * @author Administrator
 *
 */
public class ReflectUtil {

	/**
	 * 获取对象的属性描述信息
	 * @param obj
	 * @return 属性名称和属性描述的映射表
	 */
	public static Map<String,PropertyDescriptor> getPropertyDescriptorMap(Object obj){
		return getPropertyDescriptorMap( obj.getClass() );
	}
	
	/**
	 * 获取实体类的属性描述信息
	 * @param type
	 * @return 属性名称和属性描述的映射表
	 */
	public static Map<String,PropertyDescriptor> getPropertyDescriptorMap( Class<?> type ){
		Map<String,PropertyDescriptor> result = new TreeMap<String,PropertyDescriptor>();
        try {
	        final PropertyDescriptor[] pds = Introspector.getBeanInfo( type ).getPropertyDescriptors();
	        for( PropertyDescriptor pd: pds ){
		    	if( pd.getName().equalsIgnoreCase( "class" ) )
		    		continue;
		    	if (Class.class.equals( type ) && "classLoader".equals(pd.getName())) {
					// Ignore Class.getClassLoader() method - nobody needs to bind to that
					continue;
				}
		    	result.put( pd.getName(), pd );
	        }
        }
        catch( IntrospectionException e ) {
	        e.printStackTrace();
        }
        return result;
	}
	
	/**
	 * 获取指定对象的指定属性描述
	 * @param obj
	 * @param fieldName
	 * @return
	 */
	public static PropertyDescriptor getPropertyDescriptor(Object obj, String fieldName ){
		return getPropertyDescriptorMap( obj ).get( fieldName );
	}

	public static Object getPropertyValue(Object obj, String fieldName ){
		PropertyDescriptor pd = getPropertyDescriptorMap( obj ).get( fieldName );
		return getPropertyValue( obj, pd );
	}

	public static Object getPropertyValue( Object obj, PropertyDescriptor pd ) {
	    if( pd != null ){
			try {
            	Method m = pd.getReadMethod();
                if( m != null )
                	return m.invoke(obj) ;
            } catch(Exception e) {
                e.printStackTrace();
            }
		}
		return null;
    }

	/**
	 * 返回对象的属性属性和属性值的映射表
	 * @param obj 实体对象
	 * @return 对象的属性和属性值的映射表
	 */
    public static Map<String,Object> getPropertyMap(Object obj) {
		Map<String,PropertyDescriptor> props = getPropertyDescriptorMap( obj );
		Map<String,Object> result = new TreeMap<String,Object>();
		boolean isCollection = obj instanceof Map || obj instanceof Collection;
        for( Map.Entry<String,PropertyDescriptor> entry: props.entrySet() ){
			if (isCollection && "empty".equalsIgnoreCase(entry.getKey())) {
				continue;
			}
			result.put(entry.getKey(), getPropertyValue(obj, entry.getValue()));
		}
		return result;
	}
	
	/**
	 * 返回对象的部分属性map
	 * @param obj 实体对象
	 * @param propertyNameSet 需要输出的部分属性名称的集合
	 * @return 对象的属性和属性值的映射表
	 */
    public static Map<String,Object> getPropertyMap(Object obj, Set<String> propertyNameSet ) {
		return getPropertyMap( obj, propertyNameSet, false );
	}

    public static Map<String,Object> getPropertyMap(Object obj, String... propertyNames ) {
		return getPropertyMap( obj, propertyNames, false );
	}

    /**
	 * 返回对象的部分属性map
	 * @param obj 实体对象
	 * @param excludeNameSet 不需要输出的部分属性名称的集合
	 * @return 对象的属性和属性值的映射表
	 */
    public static Map<String,Object> getPropertyMapExlcude(Object obj, Set<String> excludeNameSet ) {
		return getPropertyMap( obj, excludeNameSet, true );
	}

    public static Map<String,Object> getPropertyMapExlcude(Object obj, String... propertyNames ) {
		return getPropertyMap( obj, propertyNames, true );
	}

    public static Map<String,Object> getPropertyMap(Object obj, String[] keys, boolean exclude ) {
    	Set<String> keySet = new HashSet<String>();
    	if( keys!= null && keys.length > 0 )
    		keySet.addAll( Arrays.asList( keys) );
    	return getPropertyMap( obj, keySet, exclude );
    }
    
	/**
	 * 返回对象的部分属性map
	 * @param obj 实体对象
	 * @param keySet 部分属性名称的集合
	 * @param exclude 如果为true，返回结果不包含指定keySet中指定的属性名和对应属性值，
	 * 		如果为false，则返回结果中只包含keySet中指定的属性名和对应属性值
	 * @return 对象的属性和属性值的映射表
	 */
	@SuppressWarnings("rawtypes")
    public static Map<String,Object> getPropertyMap(Object obj, Set<String> keySet, boolean exclude ) {
		Map<String,Object> result = getPropertyMap( obj );
		if( keySet == null || keySet.size()==0 )
			return result;
		
		for( Iterator iter = result.keySet().iterator(); iter.hasNext(); ){
			String key = (String)iter.next();
			if( exclude && keySet.contains( key ) )
				iter.remove();
			else if( !exclude && !keySet.contains( key ) )
				iter.remove();
		}
		return result;
	}

	/**
	 * 通过反射绕过java的访问控制，向对象中SET属性。
	 * @param target
	 * @param fieldName
	 * @param fieldValue
	 */
    public static void setFieldValue(Object target, String fieldName, Object fieldValue) {
		if (target == null || StringUtil.isEmpty( fieldName ) ) {
			return;
		}
        PropertyDescriptor pd = getPropertyDescriptor( target, fieldName );
        setFieldValue( target, fieldName, fieldValue, pd );
	}

	private static void setFieldValue( Object target, String fieldName, Object fieldValue, PropertyDescriptor pd ) {
	    Class<?> clazz = target.getClass();
		try {
		    if( pd != null ){
		    	Method m = pd.getWriteMethod();
		    	if( m != null ){
		        	m.invoke(target, fieldValue);
		        	return;
		    	}
		    }
	    	
    		StringBuilder sb = new StringBuilder( fieldName );
    		sb.setCharAt( 0, Character.toUpperCase(sb.charAt( 0 ) ) );
    		sb.insert( 0, "set" );
    		Method method = null;
    		Class<?> fieldClass = null;
    		if( fieldValue != null )
    			fieldClass = fieldValue.getClass();
    		else{
    			try {
    				Field field = clazz.getDeclaredField(fieldName);
    				fieldClass = field.getDeclaringClass();
    			}
    			catch(Exception e){
    			}
    		}
    		if( fieldClass!= null )
    			method = clazz.getDeclaredMethod( sb.toString(), fieldClass );
    		else
    			method = clazz.getDeclaredMethod( sb.toString() );
    		if (!Modifier.isPublic(method.getModifiers())) {
    			method.setAccessible(true);
    		}
    		method.invoke(target, fieldValue);
		}
		catch (Exception me) {
			try {
				Field field = clazz.getDeclaredField(fieldName);
				if (!Modifier.isPublic(field.getModifiers())) {
					field.setAccessible(true);
				}
				field.set(target, fieldValue);
			} catch (Exception fe) {
				throw new RuntimeException( "no setter method or no field:" + target.getClass().getName()
						+ ":"+fieldName +","+ fe.toString() );
			}
		}

    }
	
    /**
     * 为目标对象设置属性
     * @param target
     * @param valueMap
     */
    public static void setFieldValues(Object target, Map<String,Object> valueMap ) {
		if (target == null || valueMap == null || valueMap.size() == 0  ) {
			return;
		}
        Map<String,PropertyDescriptor> pdMap = getPropertyDescriptorMap( target );
		for( Map.Entry<String,Object> entry: valueMap.entrySet() ){
			PropertyDescriptor pd = pdMap.get( entry.getKey() );
			Object value = entry.getValue();
			if( pd == null ){
				continue;
			}
			try {
				setFieldValue( target, entry.getKey(), value, pd );
			}
			catch (Exception e) {
				e.printStackTrace();
			}
		}
	}
	
    /**
     * 复制对象中同名的属性
     * @param src
     * @param target
     */
    public static void copyProperty(Object src, Object target ) {
    	Map<String,Object> valueMap = getPropertyMap( src );
    	setFieldValues( target, valueMap );
    }
    
    /**
     * 复制对象中同名的指定属性
     * @param src
     * @param target
     * @param propertyNameSet
     */
    public static void copyProperty(Object src, Object target, Set<String> propertyNameSet ) {
    	Map<String,Object> valueMap = getPropertyMap( src, propertyNameSet );
    	setFieldValues( target, valueMap );
    }
    
    /**
     * 复制对象中同名的指定属性
     * @param src
     * @param target
     * @param propertyNames
     */
    public static void copyProperty(Object src, Object target, String... propertyNames ) {
    	Map<String,Object> valueMap = getPropertyMap( src, propertyNames );
    	setFieldValues( target, valueMap );
    }
}
